# feature_engineering.py - 修复分类数据类型问题
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler


class FeatureEngineer:
    def __init__(self, df):
        self.df = df
        self.user_features = None
        self.merchant_features = None

    def create_user_features(self):
        """创建用户特征 - 修复版本"""
        print("\n=== 创建用户特征 ===")

        # 确保数据有必要的列
        required_cols = ['用户ID', '订单ID', '实付金额', '购买数量', '商家ID', '使用状态']
        missing_cols = [col for col in required_cols if col not in self.df.columns]

        if missing_cols:
            print(f"缺少必要字段: {missing_cols}")
            return None

        try:
            user_features = self.df.groupby('用户ID').agg({
                '订单ID': 'count',  # 购买频次
                '实付金额': ['sum', 'mean', 'std'],  # 总消费额、平均消费额、消费稳定性
                '购买数量': 'sum',  # 总购买数量
                '商家ID': 'nunique',  # 购买商家数
                '使用状态': lambda x: self._calculate_coupon_usage_rate(x),  # 优惠券使用率
                '邮费': 'sum',  # 总邮费
            }).reset_index()

            # 扁平化多级列索引
            user_features.columns = ['用户ID', '购买频次', '总消费额', '平均消费额',
                                     '消费标准差', '总购买数量', '商家数', '优惠券使用率',
                                     '总邮费']

            # 添加用户的地理信息（取第一个订单的信息）
            user_geo = self.df.groupby('用户ID').first().reset_index()[['用户ID', '省份', '城市']]
            user_features = user_features.merge(user_geo, on='用户ID', how='left')

            # 时间特征
            if '付款日期' in self.df.columns:
                user_time_features = self._create_time_features()
                if user_time_features is not None:
                    user_features = user_features.merge(user_time_features, on='用户ID', how='left')

            # 地域特征
            user_features = self._create_region_features(user_features)

            # 购买行为特征
            user_features = self._create_behavior_features(user_features)

            # 处理异常值
            user_features = self._handle_feature_anomalies(user_features)

            self.user_features = user_features
            print(f"用户特征维度: {user_features.shape}")
            return user_features

        except Exception as e:
            print(f"创建用户特征时出错: {e}")
            import traceback
            traceback.print_exc()
            return None

    def create_merchant_features(self):
        """创建商家特征 - 修复版本"""
        print("\n=== 创建商家特征 ===")

        # 确保数据有必要的列
        required_cols = ['商家ID', '订单ID', '实付金额', '购买数量', '用户ID', '使用状态']
        missing_cols = [col for col in required_cols if col not in self.df.columns]

        if missing_cols:
            print(f"缺少必要字段: {missing_cols}")
            return None

        try:
            merchant_features = self.df.groupby('商家ID').agg({
                '订单ID': 'count',  # 订单数量
                '实付金额': ['sum', 'mean'],  # 总销售额、平均订单金额
                '用户ID': 'nunique',  # 客户数
                '使用状态': lambda x: self._calculate_coupon_usage_rate(x),  # 优惠券使用率
                '购买数量': 'sum'  # 总销量
            }).reset_index()

            merchant_features.columns = ['商家ID', '订单数', '总销售额', '平均订单金额',
                                         '客户数', '优惠券使用率', '总销量']

            # 处理异常值
            merchant_features = self._handle_feature_anomalies(merchant_features)

            self.merchant_features = merchant_features
            print(f"商家特征维度: {merchant_features.shape}")
            return merchant_features

        except Exception as e:
            print(f"创建商家特征时出错: {e}")
            import traceback
            traceback.print_exc()
            return None

    def _calculate_coupon_usage_rate(self, usage_series):
        """计算优惠券使用率"""
        try:
            # 只考虑已领取优惠券的用户（使用状态为0或1）
            used_coupons = (usage_series == 1).sum()
            total_coupon_orders = (usage_series.isin([0, 1])).sum()

            if total_coupon_orders > 0:
                return used_coupons / total_coupon_orders
            else:
                return 0
        except:
            return 0

    def _create_time_features(self):
        """创建时间相关特征"""
        try:
            # 确保付款日期是datetime类型
            if not pd.api.types.is_datetime64_any_dtype(self.df['付款日期']):
                self.df['付款日期'] = pd.to_datetime(self.df['付款日期'], errors='coerce')

            # 移除无效日期
            valid_dates = self.df[self.df['付款日期'].notna()]
            if len(valid_dates) == 0:
                print("无有效日期数据，跳过时间特征")
                return None

            user_time_features = valid_dates.groupby('用户ID').agg({
                '付款日期': ['min', 'max'],  # 首次和末次购买时间
            }).reset_index()

            user_time_features.columns = ['用户ID', '首次购买时间', '末次购买时间']

            # 计算时间间隔特征
            latest_date = valid_dates['付款日期'].max()
            user_time_features['最近购买间隔'] = (latest_date - user_time_features['末次购买时间']).dt.days
            user_time_features['客户生命周期'] = (
                        user_time_features['末次购买时间'] - user_time_features['首次购买时间']).dt.days

            # 月度活跃特征
            valid_dates['月份'] = valid_dates['付款日期'].dt.month
            monthly_activity = valid_dates.groupby(['用户ID', '月份'])['订单ID'].count().reset_index()
            monthly_activity = monthly_activity.groupby('用户ID')['订单ID'].nunique().reset_index()
            monthly_activity.columns = ['用户ID', '活跃月数']

            # 周末购物特征
            valid_dates['星期'] = valid_dates['付款日期'].dt.dayofweek
            weekend_orders = valid_dates[valid_dates['星期'].isin([5, 6])].groupby('用户ID')[
                '订单ID'].count().reset_index()
            weekend_orders.columns = ['用户ID', '周末订单数']

            total_orders = valid_dates.groupby('用户ID')['订单ID'].count().reset_index()
            total_orders.columns = ['用户ID', '总订单数']

            # 合并特征
            user_time_features = user_time_features.merge(monthly_activity, on='用户ID', how='left')
            user_time_features = user_time_features.merge(weekend_orders, on='用户ID', how='left')
            user_time_features = user_time_features.merge(total_orders, on='用户ID', how='left')

            # 计算周末购物比例
            user_time_features['周末购物比例'] = user_time_features['周末订单数'] / user_time_features['总订单数']
            user_time_features['周末购物比例'] = user_time_features['周末购物比例'].fillna(0)

            # 选择需要的列
            user_time_features = user_time_features[
                ['用户ID', '最近购买间隔', '客户生命周期', '活跃月数', '周末购物比例']]

            return user_time_features

        except Exception as e:
            print(f"创建时间特征时出错: {e}")
            return None

    def _create_region_features(self, user_features):
        """创建地域相关特征"""
        try:
            region_stats = self.df.groupby('省份').agg({
                '实付金额': ['mean', 'sum'],
                '订单ID': 'count',
                '用户ID': 'nunique'
            }).reset_index()

            region_stats.columns = ['省份', '省份平均消费', '省份总消费', '省份订单数', '省份用户数']

            # 计算省份消费水平等级
            if len(region_stats) >= 3:
                try:
                    region_stats['省份消费等级'] = pd.qcut(region_stats['省份平均消费'], 3, labels=['低', '中', '高'])
                except:
                    # 如果分位数切割失败，使用简单分类
                    region_stats['省份消费等级'] = np.where(
                        region_stats['省份平均消费'] > region_stats['省份平均消费'].median(), '高', '低'
                    )
            else:
                region_stats['省份消费等级'] = '中'  # 省份太少时默认中等

            user_features = user_features.merge(region_stats, on='省份', how='left')
            return user_features
        except Exception as e:
            print(f"创建地域特征时出错: {e}")
            return user_features

    def _create_behavior_features(self, user_features):
        """创建购买行为特征"""
        try:
            # 避免除零错误
            user_features['客单价'] = np.where(
                user_features['购买频次'] > 0,
                user_features['总消费额'] / user_features['购买频次'],
                0
            )

            user_features['单次购买数量'] = np.where(
                user_features['购买频次'] > 0,
                user_features['总购买数量'] / user_features['购买频次'],
                0
            )

            user_features['商家多样性'] = np.where(
                user_features['购买频次'] > 0,
                user_features['商家数'] / user_features['购买频次'],
                0
            )

            # 处理无穷值
            user_features = user_features.replace([np.inf, -np.inf], np.nan)

            return user_features
        except Exception as e:
            print(f"创建行为特征时出错: {e}")
            return user_features

    def _handle_feature_anomalies(self, features_df):
        """处理特征异常值 - 修复分类数据类型问题"""
        try:
            # 复制数据框避免修改原始数据
            features_df = features_df.copy()

            # 分别处理数值列和分类列
            numeric_cols = features_df.select_dtypes(include=[np.number]).columns
            categorical_cols = features_df.select_dtypes(include=['object', 'category']).columns

            print(f"处理数值列: {list(numeric_cols)}")
            print(f"处理分类列: {list(categorical_cols)}")

            # 处理数值列的缺失值
            for col in numeric_cols:
                if features_df[col].isna().any():
                    # 用中位数填充数值列
                    median_val = features_df[col].median()
                    features_df[col] = features_df[col].fillna(median_val)
                    print(f"  数值列 {col}: 用中位数 {median_val:.2f} 填充缺失值")

            # 处理分类列的缺失值
            for col in categorical_cols:
                if features_df[col].isna().any():
                    # 用众数填充分类列
                    mode_val = features_df[col].mode()
                    if len(mode_val) > 0:
                        fill_value = mode_val.iloc[0]
                        features_df[col] = features_df[col].fillna(fill_value)
                        print(f"  分类列 {col}: 用众数 '{fill_value}' 填充缺失值")
                    else:
                        # 如果没有众数，用第一个非空值填充
                        first_valid = features_df[col].dropna().iloc[0] if not features_df[col].dropna().empty else '未知'
                        features_df[col] = features_df[col].fillna(first_valid)
                        print(f"  分类列 {col}: 用第一个值 '{first_valid}' 填充缺失值")

            # 处理无穷值
            for col in numeric_cols:
                if col in features_df.columns:
                    features_df[col] = features_df[col].replace([np.inf, -np.inf], np.nan)
                    # 再次填充可能出现的NaN
                    if features_df[col].isna().any():
                        median_val = features_df[col].median()
                        features_df[col] = features_df[col].fillna(median_val)

            return features_df

        except Exception as e:
            print(f"处理特征异常值时出错: {e}")
            import traceback
            traceback.print_exc()
            return features_df

    def get_feature_summary(self):
        """获取特征摘要"""
        if self.user_features is not None:
            print("\n=== 用户特征摘要 ===")
            print(f"用户特征数量: {len(self.user_features.columns)}")
            print("用户特征列表:")
            for col in self.user_features.columns:
                dtype = self.user_features[col].dtype
                non_null = self.user_features[col].count()
                unique = self.user_features[col].nunique() if self.user_features[col].dtype == 'object' else 'N/A'
                print(f"  {col} ({dtype}) - 非空值: {non_null}, 唯一值: {unique}")

        if self.merchant_features is not None:
            print(f"\n商家特征数量: {len(self.merchant_features.columns)}")
            print("商家特征列表:")
            for col in self.merchant_features.columns:
                dtype = self.merchant_features[col].dtype
                non_null = self.merchant_features[col].count()
                unique = self.merchant_features[col].nunique() if self.merchant_features[
                                                                      col].dtype == 'object' else 'N/A'
                print(f"  {col} ({dtype}) - 非空值: {non_null}, 唯一值: {unique}")